#ifndef VECTOR4TEM_H
#define VECTOR4TEM_H

#define SIZE 4

#include "imMaths.h"
#include "Vector3.h"
#include "Matrix4.h"

template <typename Real>
struct Vector4Tem {
public:
	union {
		struct {
			Real x, y, z, w;
		};
		Real Data[4];
	};

	// Constructors
	Vector4Tem(void) {
		memset(&Data, 0, sizeof(Real) * 4);
	}
	Vector4Tem(Real _x, Real _y, Real _z) {
		x = _x;
		y = _y;
		z = _z;
		w = 1.0;
	}
	Vector4Tem(Real _x, Real _y, Real _z, Real _w) {
		x = _x;
		y = _y;
		z = _z;
		w = _w;
	}

	Vector4Tem(Vector3Tem<Real> a) {
		x = a.x;
		y = a.y;
		z = a.z;
		w = 1.0;
	}

	// Members
	Vector4Tem<Real> Cross(const Vector4Tem<Real> &a) { // Returns Cross Product of this and a
		return Vector4Tem<Real>((y*a.z - z*a.y),
							 (z*a.x - x*a.z),
							 (x*a.y - y*a.x));
	}
	Vector4Tem<Real> ComponentProduct(const Vector4Tem<Real> &a) { // Multiply each component of the vector together
		return Vector4Tem<Real>(x * a.x,
							 y * a.y,
							 z * a.z);
	}
	Real Dot(const Vector4Tem<Real> &a) { // Dot product
		return x*a.x + y*a.y + z*a.z;
	}
	Real LengthSqr(void) { // Returns length^2 of vector. Note that this is faster than Length()
		return x*x + y*y + z*z;
	}
	Real Length(void) { // Returns length of vector
		return sqrt(LengthSqr());
	}
	void Normalize(void) { // Converts vector to unit length
		Real l = Length();
		this /= l;
	}
	void AddScaledVector(const Vector4Tem<Real> &a, const Real &s) {
		x += a.x * s;
		y += a.y * s;
		z += a.z * s;
	}
	
	// Operators! Woo!
	Real operator*(const Vector4Tem<Real> &a) { // Dot product
		return Dot(a);
	}
		
	Vector4Tem<Real> operator+(const Vector4Tem<Real> &a) {
		return Vector4Tem<Real> (x + a.x, y + a.y, z + a.z);
	}
	Vector4Tem<Real> operator-(const Vector4Tem<Real> &a) {
		return Vector4Tem<Real> (x - a.x, y - a.y, z - a.z);
	}
	Vector4Tem<Real> & operator+=(const Vector4Tem<Real> &a) {
		x += a.x;
		y += a.y;
		z += a.z;
		return *this;
	}
	Vector4Tem<Real> & operator-=(const Vector4Tem<Real> &a) {
		x -= a.x;
		y -= a.y;
		z -= a.z;
		return *this;
	}
		
	Vector4Tem<Real> operator*(const Real &a) {
		return Vector4Tem<Real> (x * a, y * a, z * a);
	}
	Vector4Tem<Real> operator/(const Real &a) {
		return Vector4Tem<Real> (x / a, y / a, z / a);
	}
	Vector4Tem<Real> & operator*=(const Real &a) {
		x *= a;
		y *= a;
		z *= a;
		return *this;
	}
	Vector4Tem<Real> & operator/=(const Real &a) {
		x /= a;
		y /= a;
		z /= a;
		return *this;
	}

	bool operator== (const Vector4Tem<Real> &v) {
		Vector4Tem<Real> p = this->operator-(v);
		return fabs(p.x) < FLT_EPSILON && fabs(p.y) < FLT_EPSILON && fabs(p.z) < FLT_EPSILON && fabs(p.w) < FLT_EPSILON;
	}

	operator const Vector4Tem<float>(void) {
		Vector4Tem<float> o;

		for(int i = 0; i < SIZE; i++)
			o.Data[i] = (float)Data[i];

		return o;
	}

	operator const Vector4Tem<double>(void) {
		Vector4Tem<double> o;

		for(int i = 0; i < SIZE; i++)
			o.Data[i] = (double)Data[i];

		return o;
	}

	operator const Vector3Tem<Real>(void) {
		return Vector3Tem<Real> (x, y, z);
	}
	
	Vector4Tem<Real> operator* (const Matrix4Tem<Real> &m) {
		Vector4Tem<Real> Out;

		for(int i = 0; i < SIZE; i++) {
			for(int j = 0; j < SIZE; j++) {
				Out.Data[i] += m.Data[i][j] * Data[j];
			}
		}

		return Out;
	}
};

#undef SIZE

typedef Vector4Tem<float> Vector4;
typedef Vector4Tem<float> Vector4f;
typedef Vector4Tem<double> Vector4d;

#endif